抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

18. Ansible Playbook Include

目录

说明

在前面的学习当中,我们一直使用一个playbook文件来组织所有的task任务。但是,当我们项目越来越大,task越来越多的时候,如果还将所有的task都写到一个playbook当中,可读性就会变差,这个时候我们就需要重新来组织playbook了。

我们可以将一个大的playbook拆成若干个小的playbook文件,在主配置文件中将这些零碎的小文件引入进来,而这种方式就叫做playbook的”include”。

include

playbook的include其实就是使用include关键字

tasks include

1. include简单示例

下面是两个playbook示例,分别用于安装lamp和lnmp环境:

# cat lamp.yml
- hosts: test
gather_facts: no
tasks:
- package:
name: mysql
state: present
- package:
name: php-fpm
state: present
- package:
name: httpd
state: present
# cat lnmp.yml
- hosts: test
gather_facts: no
tasks:
- package:
name: mysql
state: present
- package:
name: php-fpm
state: present
- package:
name: nginx
state: present

在上面的示例当中,我们可以看到lamp和lnmp中mysql和php的安装都是一样的,所以我们可以将这两个任务提取出来,放到一个单独的task文件中,然后在lnmp和lamp中引入:

# cat install_mysql_php.yml
- package:
name: mysql
state: present
- package:
name: php-fpm
state: present
# cat lamp.yml
- hosts: test
gather_facts: no
tasks:
- include: install_mysql_php.yml
- package:
name: httpd
state: php-fpm
# cat lnmp.yml
- hosts: test
gather_facts: no
tasks:
- include: install_mysql_php.yml
- package:
name: nginx
state: php-fpm

2. 在include时引入变量

也可以在include的时候,传入变量:

# cat test_include.yml
- hosts: test
gather_facts: no
tasks:
- include: wordpress.yml user=timmy
- include: wordpress.yml user=alice
- include: wordpress.yml user=bob
# cat wordpress.yml
- debug:
msg: "{{ user }}"

通过如下方式带入变量:

tasks:
- { include: wordpress.yml, user: timmy, ssh_keys: [ 'keys/one.txt', 'keys/two.txt' ] }

再给一个例子:

- hosts: test
gather_facts: no
tasks:
- include: in.yml
vars:
users:
bob:
gender: male
lucy:
gender: female
# cat in.yml
- debug:
msg: "{{ item.key }} is {{ item.value.gender }}"
loop: "{{users | dict2items }}""

3. 在include中使用tag

# cat test_include.yml
- hosts: test
gather_facts: no
tasks:
- include: in1.yml
tags: t1
- include: in2.yml
tags: t2
# cat in1.yml
- debug:
msg: "task1 in in1.yml"
- debug:
msg: "task2 in in1.yml"
# cat in2.yml
- debug:
msg: "task1 in in2.yml"
- debug:
msg: "task2 in in2.yml"

在上面的示例当中,两个Include分别对应两个tag,如果我们在执行test_include.yml时,指定tag为t2,那么in2.yml中的所有任务都会被执行。所以tag是针对include的所有任务生效。

4. 在include中使用条件判断

# cat test_include.yml
- hosts: test
gather_facts: no
tasks:
- include: in.yml
when: 2 > 1
# cat in.yml
- debug:
msg: "task in in.yml"

5. 在include中使用循环

下面是一个简单的循环示例:

# cat test_include.yml
- hosts: test
gather_facts: no
tasks:
- include: in.yml
loop:
- 1
- 2
# cat in.yml
- debug:
msg: "task1 in in.yml"
- debug:
msg: "task2 in in.yml"

可以看到in.yml被循环执行了两次。

我们可以稍微修改in.yml示例如下:

# cat in.yml
- debug:
msg: "{{ item }} task1 in in.yml"
- debug:
msg: "{{ item }} task2 in in.yml"

再次执行playbook的结果如下:

[root@workstation ansible]# ansible-playbook test_include.yml
PLAY [servera] **********************************************************************************************************************************
TASK [include] **********************************************************************************************************************************
included: /etc/ansible/in.yml for servera => (item=1)
included: /etc/ansible/in.yml for servera => (item=2)
TASK [debug] ************************************************************************************************************************************
ok: [servera] => {
"msg": "1 task1 in in.yml"
}
TASK [debug] ************************************************************************************************************************************
ok: [servera] => {
"msg": "1 task2 in in.yml"
}
TASK [debug] ************************************************************************************************************************************
ok: [servera] => {
"msg": "2 task1 in in.yml"
}
TASK [debug] ************************************************************************************************************************************
ok: [servera] => {
"msg": "2 task2 in in.yml"
}
PLAY RECAP **************************************************************************************************************************************
servera : ok=6 changed=0 unreachable=0 failed=0

可以看到item的值就来自test_include中的loop循环。那么这就引出了一个问题:如果正好in.yml当中也有循环时怎么办?

# cat in.yml
- debug:
msg: "{{ item }} task1 in in.yml"
loop: ['a','b','c']

再次执行test_include,结果如下:

[root@workstation ansible]# ansible-playbook test_include.yml
PLAY [servera] **********************************************************************************************************************************
TASK [include] **********************************************************************************************************************************
included: /etc/ansible/in.yml for servera => (item=1)
included: /etc/ansible/in.yml for servera => (item=2)
TASK [debug] ************************************************************************************************************************************
[WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value in the `loop_control` option for the task to
something else to avoid variable collisions and unexpected behavior.
ok: [servera] => (item=a) => {
"msg": "a task1 in in.yml"
}
ok: [servera] => (item=b) => {
"msg": "b task1 in in.yml"
}
ok: [servera] => (item=c) => {
"msg": "c task1 in in.yml"
}
TASK [debug] ************************************************************************************************************************************
[WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value in the `loop_control` option for the task to
something else to avoid variable collisions and unexpected behavior.
ok: [servera] => (item=a) => {
"msg": "a task1 in in.yml"
}
ok: [servera] => (item=b) => {
"msg": "b task1 in in.yml"
}
ok: [servera] => (item=c) => {
"msg": "c task1 in in.yml"
}
PLAY RECAP **************************************************************************************************************************************
servera : ok=4 changed=0 unreachable=0 failed=0

这个时候,可以看到最终item的值来自in.yml中的循环。那如果我就想要使用test_include中的循环的值怎么办? 我们再次修改test_include.yml以及in.yml如下:

# cat test_include.yml
- hosts: test
gather_facts: no
tasks:
- include: in.yml
loop:
- 1
- 2
loop_control:
loop_var: outer_item
# cat in.yml
- debug:
msg: "{{outer_item }} {{ item }} task1 in in.yml"
loop: ['a','b','c']

再次查看结果:

PLAY [servera] **********************************************************************************************************************************
TASK [include] **********************************************************************************************************************************
included: /etc/ansible/in.yml for servera
included: /etc/ansible/in.yml for servera
TASK [debug] ************************************************************************************************************************************
ok: [servera] => (item=a) => {
"msg": "1 a task1 in in.yml"
}
ok: [servera] => (item=b) => {
"msg": "1 b task1 in in.yml"
}
ok: [servera] => (item=c) => {
"msg": "1 c task1 in in.yml"
}
TASK [debug] ************************************************************************************************************************************
ok: [servera] => (item=a) => {
"msg": "2 a task1 in in.yml"
}
ok: [servera] => (item=b) => {
"msg": "2 b task1 in in.yml"
}
ok: [servera] => (item=c) => {
"msg": "2 c task1 in in.yml"
}
PLAY RECAP **************************************************************************************************************************************
servera : ok=4 changed=0 unreachable=0 failed=0

可以看到,outer_item中的值正是外层循环中item的值。当出现这个双层循环时,可以在外层循环中使用loop_var选项指定一个变量,这个变量用于替代外层循环中的item变量,以便在内层循环中获取到外层循环的item的值,从而避免两层循环中item变量名的冲突。

handlers include

handlers include与tasks include大体类似,直接给例子:

# handlers1.yml内容如下:
# this might be in a file like handlers/handlers.yml
- name: restart apache
service:
name: apache
state: restarted
# handlers.yml包含handlers1.yml示例:
handlers:
- include: handlers/handlers.yml

playbook include

include也可以用于将一个playbook导入到另一个playbook中:

- name: this is a play at the top level of a file
hosts: all
remote_user: root
tasks:
- name: say hi
tags: foo
shell: echo "hi..."
- include: load_balancers.yml
- include: webservers.yml
- include: dbservers.yml

include_tasks

基本使用

在前面我们详细说了include的用法,然而事实上在后续的ansible版本当中,include语法可能会被弃用。而使用一些新的关键字来代替include的原始用法,include_tasks就是其中之一。

我们知道include可以用于包含tasks,handlers,playbooks等,而include_tasks则专门用于包含tasks:

# cat include_tasks_ex.yml
- hosts:
gather_facts: no
tasks:
- debug:
msg: "task1"
- include_tasks: in.yml
- debug:
msg: "task2"
# cat in.yml
- debug:
msg: "{{ item }} task1 in in.yml"
- debug:
msg: "{{ item }} task2 in in.yml"

执行结果如下:

PLAY [servera] **********************************************************************************************************************************
TASK [debug] ************************************************************************************************************************************
ok: [servera] => {
"msg": "task1"
}
TASK [include_tasks] ****************************************************************************************************************************
included: /etc/ansible/in.yml for servera
TASK [debug] ************************************************************************************************************************************
ok: [servera] => {
"msg": "task1 in in.yml"
}
TASK [debug] ************************************************************************************************************************************
ok: [servera] => {
"msg": "task2 in in.yml"
}
TASK [debug] ************************************************************************************************************************************
ok: [servera] => {
"msg": "task2"
}
PLAY RECAP **************************************************************************************************************************************
servera : ok=5 changed=0 unreachable=0 failed=0

可以看到,当我们使用include_tasks时,include_tasks本身会被当做一个task,这个task会把include的文件的路径输出在控制台中中, 这就是include_tasksinclude之间的区别。include是透明的,而include_tasks是可见的,include_tasks更像是一个任务,这个任务包含了其他的一些任务。

在ansible 2.7版本当中,include_tasks还加入了新的参数,下面是一个简单用法示例:

include_tasks:
file: in.yml

当然这种使用方法与include_tasks: in.yml的效果完全相同。

在include_tasks中使用tags

在前面我们提到过,如果为include添加tags,那么tags是对include中所有任务生效的。也就是说,如果调用include对应的tag,那么include文件中的所有任务都会执行。

但是对include_tasks添加tags,则只会对include_tasks本身生效,include_tasks中所有的任务都不生效。示例如下:

# cat include_tasks_ex.yml
- hosts: test
gather_facts: no
tasks:
- debug:
msg: "test task1"
- include_tasks:
file: in.yml
tags: t1
- debug:
msg: "test task3"
# cat in.yml
- debug:
msg: "test task2"

执行结果如下:

# ansible-playbook include_tasks_ex.yml --tags t1
PLAY [test] ******************************************************************************************************
TASK [include_tasks] *********************************************************************************************
included: /etc/ansible/in.yml for 10.1.61.187
PLAY RECAP *******************************************************************************************************
10.1.61.187 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

如果想要tags对include_tasks中包含的所有任务生效,则需要使用include_tasks模块的apply参数并配合tags: always内置tag:

- hosts: test
gather_facts: no
tasks:
- debug:
msg: "test task1"
- include_tasks:
file: in.yml
apply:
tags: t1
tags: always
- debug:
msg: "test task3"

执行结果:

# ansible-playbook include_tasks_ex.yml --tags t1
PLAY [test] ******************************************************************************************************
TASK [include_tasks] *********************************************************************************************
included: /etc/ansible/in.yml for 10.1.61.187
TASK [debug] *****************************************************************************************************
ok: [10.1.61.187] => {
"msg": "test task2"
}
PLAY RECAP *******************************************************************************************************
10.1.61.187 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

在上一篇我们讲到tags的时候说过,如果一个任务被打上了tags: always标签,则即使我们调用其他任务的标签,该任务也会被执行。

需要说明的是,在这里,tags: always标签只针对include_tasks本身生效,也就是说,如果其他任务的标签被调用,include_tasks本身会被调用,而其包含的任务不会被调用。如果要想其包含的任务也总是被调用,可修改配置如下:

- hosts: test
gather_facts: no
tasks:
- debug:
msg: "test task1"
- include_tasks:
file: in.yml
apply:
tags: t1,always
tags: always
- debug:
msg: "test task3"

import_tasks

import_tasksinclude_tasks用法类似,都用于包含一个任务列表:

# cat import_tasks_ex.yml
- hosts: test
gather_facts: no
tasks
- debug:
msg: "test task1"
- import_tasks: in.yml
# cat in.yml
- debug:
msg: "test task2"

执行结果:

# ansible-playbook import_tasks_ex.yml
PLAY [test] ******************************************************************************************************
TASK [debug] *****************************************************************************************************
ok: [10.1.61.187] => {
"msg": "test task1"
}
TASK [debug] *****************************************************************************************************
ok: [10.1.61.187] => {
"msg": "test task2"
}
PLAY RECAP *******************************************************************************************************
10.1.61.187 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

可以看到,import_tasks模块并不会像include_tasks模块一样,在控制台输出自身的任务信息,其相对透明。

除此之外,import_tasksinclude_tasks还有如下不同:

  1. import_tasks是静态的,被import的文件在playbook被加载时就预处理了,而include_tasks是动态的,被include的文件在playbook被运行时候才开始处理。一个简单的例子:

    - hosts: test
    gather_facts: no
    vars:
    file_name: in.yml
    tasks:
    - include_tasks: {{ file_name }}
    - import_tasks: {{ file_name }}

    在上面的示例中,include_tasksimport_tasks均会被执行。

    再看下面的例子:

    - hosts: test
    gather_facts: no
    tasks:
    - set_fact:
    file_name: in.yml
    - include_tasks: {{ file_name }}
    - import_tasks: {{ file_name }}

    此时,import_tasks就会出错:

    # ansible-playbook include_import_tasks_ex.yml
    ERROR! Error when evaluating variable in import path: {{ file_name }}.
    When using static imports, ensure that any variables used in their names are defined in vars/vars_files
    or extra-vars passed in from the command line. Static imports cannot use variables from facts or inventory
    sources like group or host vars.

    当使用静态的import时,请确保文件名中使用到的变量被定义在vars、vars_files或者extra-vars中,不支持其他的方式传入变量。

  2. 如果想要对包含的任务列表进行循环操作,则只能使用include_tasksimport_tasks不支持循环操作。也就是说,使用loop或者with_X对include文件进行循环操作时,只能配合include_tasks才能正常使用

  3. 当使用when对include文件添加条件判断时,include_tasksimport_tasks

有着本质的不同:

  • 当对include_tasks使用when时,when对应的条件只会应用于include_tasks任务本身,当执行被包含的任务时,不会对这些被包含的任务重新进行条件判断

  • 当对import_tasks使用when时,when对应的条件会被应用于被import的文件中的每一个任务,当执行被import的任务时,会对每一个被包含的任务进行同样的条件判断。

示例如下:

# cat include_import_tasks_ex2.yml
- hosts: test
gather_facts: no
tasks:
- name: set testvar to 0
set_fact:
testnum: 0
- debug:
msg: 'include_tasks: in1.yml'
- include_tasks: in1.yml
when: testnum == 0
- name: set testvar to 0
set_fact:
testnum: 0
- debug:
msg: 'import_tasks: in1.yml'
- import_tasks: in1.yml
when: testnum == 0

执行结果:

# ansible-playbook include_import_tasks_ex2.yml
PLAY [test] ******************************************************************************************************
TASK [set testvar to 0] ******************************************************************************************
ok: [10.1.61.187]
TASK [debug] *****************************************************************************************************
ok: [10.1.61.187] => {
"msg": "include_tasks: in1.yml"
}
TASK [include_tasks] *********************************************************************************************
included: /etc/ansible/in1.yml for 10.1.61.187
TASK [set_fact] **************************************************************************************************
ok: [10.1.61.187]
TASK [debug] *****************************************************************************************************
ok: [10.1.61.187] => {
"msg": "test task2"
}
TASK [set testvar to 0] ******************************************************************************************
ok: [10.1.61.187]
TASK [debug] *****************************************************************************************************
ok: [10.1.61.187] => {
"msg": "import_tasks: in1.yml"
}
TASK [set_fact] **************************************************************************************************
ok: [10.1.61.187]
TASK [debug] *****************************************************************************************************
skipping: [10.1.61.187]
PLAY RECAP *******************************************************************************************************
10.1.61.187 : ok=8 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0

在handlers中使用include_tasks及import_tasks

我们知道,handlers中执行的其实也是任务,只不过是被触发才会运行,所以如果要在handlers中引入任务,也可直接使用include_tasksimport_tasks。没有include_handlers的说法。

import_playbook

我们在前面提到过,include除了可以引用任务列表,还可以引用整个playbook,在之后的版本中,如果想要引入playbook,则需要使用import_playbook模块。在2.8版本后,使用include引用整个playbook的特性会被弃用。

示例:

# cat import_playbook_ex.yml
- hosts: test
gather_facts: no
tasks:
- debug:
msg: "test task"
- import_playbook: inplay.yml
# cat inplay.yml
- hosts: test
gather_facts: no
tasks:
- debug:
msg: "test task in inplay.yml"

评论